<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Cache\Driver;

use Think\Cache;

defined ( 'THINK_PATH' ) or exit ();
/**
 * Shmop缓存驱动
 */
class Shmop extends Cache {
	
	/**
	 * 架构函数
	 * 
	 * @param array $options
	 *        	缓存参数
	 * @access public
	 */
	public function __construct($options = array()) {
		if (! extension_loaded ( 'shmop' )) {
			E ( L ( '_NOT_SUPPORT_' ) . ':shmop' );
		}
		if (! empty ( $options )) {
			$options = array (
					'size' => C ( 'SHARE_MEM_SIZE' ),
					'temp' => TEMP_PATH,
					'project' => 's',
					'length' => 0 
			);
		}
		$this->options = $options;
		$this->options ['prefix'] = isset ( $options ['prefix'] ) ? $options ['prefix'] : C ( 'DATA_CACHE_PREFIX' );
		$this->options ['length'] = isset ( $options ['length'] ) ? $options ['length'] : 0;
		$this->handler = $this->_ftok ( $this->options ['project'] );
	}
	
	/**
	 * 读取缓存
	 * 
	 * @access public
	 * @param string $name
	 *        	缓存变量名
	 * @return mixed
	 */
	public function get($name = false) {
		N ( 'cache_read', 1 );
		$id = shmop_open ( $this->handler, 'c', 0600, 0 );
		if ($id !== false) {
			$ret = unserialize ( shmop_read ( $id, 0, shmop_size ( $id ) ) );
			shmop_close ( $id );
			
			if ($name === false) {
				return $ret;
			}
			$name = $this->options ['prefix'] . $name;
			if (isset ( $ret [$name] )) {
				$content = $ret [$name];
				if (C ( 'DATA_CACHE_COMPRESS' ) && function_exists ( 'gzcompress' )) {
					// 启用数据压缩
					$content = gzuncompress ( $content );
				}
				return $content;
			} else {
				return null;
			}
		} else {
			return false;
		}
	}
	
	/**
	 * 写入缓存
	 * 
	 * @access public
	 * @param string $name
	 *        	缓存变量名
	 * @param mixed $value
	 *        	存储数据
	 * @return boolean
	 */
	public function set($name, $value) {
		N ( 'cache_write', 1 );
		$lh = $this->_lock ();
		$val = $this->get ();
		if (! is_array ( $val ))
			$val = array ();
		if (C ( 'DATA_CACHE_COMPRESS' ) && function_exists ( 'gzcompress' )) {
			// 数据压缩
			$value = gzcompress ( $value, 3 );
		}
		$name = $this->options ['prefix'] . $name;
		$val [$name] = $value;
		$val = serialize ( $val );
		if ($this->_write ( $val, $lh )) {
			if ($this->options ['length'] > 0) {
				// 记录缓存队列
				$this->queue ( $name );
			}
			return true;
		}
		return false;
	}
	
	/**
	 * 删除缓存
	 * 
	 * @access public
	 * @param string $name
	 *        	缓存变量名
	 * @return boolean
	 */
	public function rm($name) {
		$lh = $this->_lock ();
		$val = $this->get ();
		if (! is_array ( $val ))
			$val = array ();
		$name = $this->options ['prefix'] . $name;
		unset ( $val [$name] );
		$val = serialize ( $val );
		return $this->_write ( $val, $lh );
	}
	
	/**
	 * 生成IPC key
	 * 
	 * @access private
	 * @param string $project
	 *        	项目标识名
	 * @return integer
	 */
	private function _ftok($project) {
		if (function_exists ( 'ftok' ))
			return ftok ( __FILE__, $project );
		if (strtoupper ( PHP_OS ) == 'WINNT') {
			$s = stat ( __FILE__ );
			return sprintf ( "%u", (($s ['ino'] & 0xffff) | (($s ['dev'] & 0xff) << 16) | (($project & 0xff) << 24)) );
		} else {
			$filename = __FILE__ . ( string ) $project;
			for($key = array (); sizeof ( $key ) < strlen ( $filename ); $key [] = ord ( substr ( $filename, sizeof ( $key ), 1 ) ))
				;
			return dechex ( array_sum ( $key ) );
		}
	}
	
	/**
	 * 写入操作
	 * 
	 * @access private
	 * @param string $name
	 *        	缓存变量名
	 * @return integer|boolean
	 */
	private function _write(&$val, &$lh) {
		$id = shmop_open ( $this->handler, 'c', 0600, $this->options ['size'] );
		if ($id) {
			$ret = shmop_write ( $id, $val, 0 ) == strlen ( $val );
			shmop_close ( $id );
			$this->_unlock ( $lh );
			return $ret;
		}
		$this->_unlock ( $lh );
		return false;
	}
	
	/**
	 * 共享锁定
	 * 
	 * @access private
	 * @param string $name
	 *        	缓存变量名
	 * @return boolean
	 */
	private function _lock() {
		if (function_exists ( 'sem_get' )) {
			$fp = sem_get ( $this->handler, 1, 0600, 1 );
			sem_acquire ( $fp );
		} else {
			$fp = fopen ( $this->options ['temp'] . $this->options ['prefix'] . md5 ( $this->handler ), 'w' );
			flock ( $fp, LOCK_EX );
		}
		return $fp;
	}
	
	/**
	 * 解除共享锁定
	 * 
	 * @access private
	 * @param string $name
	 *        	缓存变量名
	 * @return boolean
	 */
	private function _unlock(&$fp) {
		if (function_exists ( 'sem_release' )) {
			sem_release ( $fp );
		} else {
			fclose ( $fp );
		}
	}
}
